﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RWLibrary.Logic;
using RWLibrary.Logic.BooleanExpression;
using RWLibrary.Logic.Graph;
using RWLibrary.Logic.Queries;
using RWLibrary.Logic.Statements;

namespace RWLibrary.Parser
{
    public class QueryParser
    {
        private QueryToken token;
        private QueryScanner scanner;
        //private World world;
        private Graph graph;

        public QueryParser(Graph graph)
        {
            this.graph = graph;
        }

        //public void ParseQueries(string[] lines)
        //{
        //    StatementManager manager = world.Statements;
        //    for (int i = 1; i <= lines.Length;i++)
        //    {
        //        Statement st;
        //        try
        //        {
        //            st = ParseLine(lines[i - 1]);
        //        }
        //        catch (Exception ex)
        //        {
        //            throw new ParserException(string.Format("There was a problem with parse at line: {0} \n{1}", i,
        //                                                    ex.Message));
        //        }
        //    }
        //}

        public Query ParseQuery(string s)
        {
            scanner = new QueryScanner(s);

            token = scanner.Scan();
            switch (token.kind)
            {
                case QueryToken.Necessary:
                    token = scanner.Scan();
                    if (token.kind == QueryToken.Cost)
                        return ParseGoalQuery(new NecessaryGoalQuery(graph));

                    return ParseValueQuery(new NecessaryValueQuery(graph));

                case QueryToken.Possibly:
                    token = scanner.Scan();
                    if (token.kind == QueryToken.Cost)
                        return ParseGoalQuery(new PossiblyGoalQuery(graph));
                    return ParseValueQuery(new PossiblyValueQuery(graph));
                case QueryToken.Accessible:
                    return ParseAccessibilityQuery(new AccessibilityQuery(graph) );
            }
            throw new ParserException(string.Format("Unexpected string at 0"));

        }

        private Query ParseAccessibilityQuery(AccessibilityQuery accessibilityQuery)
        {
            accessibilityQuery.Goal = Expression();
            if (token.kind != QueryToken.After)
                throw new ParserException(string.Format("After keyword expected at {0}", scanner.Pos));
            token = scanner.Scan();
            accessibilityQuery.Actions = ActionList();
            token = scanner.Scan();
            if(token.kind == QueryToken.From)
                accessibilityQuery.Pi = Expression();
            else
                if (token.kind == QueryToken.Eof)
                {
                    if (graph.World.Statements.Initially == null)
                        throw new ParserException(string.Format("World description did not have initially statement"));
                    accessibilityQuery.Pi = graph.World.Statements.Initially.Alpha;
                }
                else
                    throw new ParserException(string.Format("From keyword expected at {0}", scanner.Pos));
            return accessibilityQuery;
        }


        private Query ParseValueQuery(ValueQuery valueQuery)
        {
            valueQuery.Alpha = ExpressionScanned();
            if (token.kind != QueryToken.After)
                throw new ParserException(string.Format("After keyword expected at {0}", scanner.Pos));
            token = scanner.Scan();
            valueQuery.Actions = ActionList();
            token = scanner.Scan();
            if (token.kind != QueryToken.From)
                throw new ParserException(string.Format("From keyword expected at {0}", scanner.Pos));
            valueQuery.Pi = Expression();
            if (token.kind == QueryToken.With)
            {
                token = scanner.Scan();
                if (token.kind != QueryToken.Cost)
                    throw new ParserException(string.Format("Cost keyword expected at {0}", scanner.Pos));
                token = scanner.Scan();
                if (token.kind != QueryToken.Number)
                    throw new ParserException(string.Format("Cost value expected at {0}", scanner.Pos));
                valueQuery.Cost = int.Parse(token.val);
            }

            return valueQuery;
        }

        private Query ParseGoalQuery(GoalQuery goalQuery)
        {
            token = scanner.Scan();
            if(token.kind != QueryToken.Number)
                throw new ParserException(string.Format("Cost value expected at {0}", scanner.Pos));
            goalQuery.Cost = int.Parse(token.val);
            token = scanner.Scan();
            if (token.kind != QueryToken.Goal)
                throw new ParserException(string.Format("Goal keyword expected at {0}", scanner.Pos));
            goalQuery.Goal = Expression();

            if(graph.World.Statements.Initially== null)
                throw new ParserException(string.Format("World description did not have initially statement"));
            return goalQuery;
        }

        private List<string> ActionList()
        {
            List<string> result = new List<string>(); 
         //   token = scanner.Scan();
            if(token.kind != QueryToken.Open_par)
                throw new ParserException(string.Format("Open parenthese for list of actions expected at {0}",scanner.Pos));
            token = scanner.Scan();
            while(token.kind == QueryToken.Ident)
            {
                if(!graph.World.ContainsAction(token.val))
                    throw new ParserException(string.Format("Not known action name at {0}", scanner.Pos));
                result.Add(token.val);
                token = scanner.Scan();
            }
            if(token.kind != QueryToken.Close_par)
                throw new ParserException(string.Format("Close parenthese for list of actions expected at {0}", scanner.Pos));
            return result;
        }
        

        private IBooleanExpression Expression()
        {
            token = scanner.Scan();

            return ExpressionScanned();
        }

        private IBooleanExpression ExpressionScanned()
        {
            return Or();
        }

        private IBooleanExpression Or()
        {
            IBooleanExpression left = And();
            if (token.kind == QueryToken.Or)
            {
                token = scanner.Scan();
                return new Or(left, Or());
            }

            return left;
        }

        private IBooleanExpression Negation()
        {
            IBooleanExpression expression;
            if (token.kind == QueryToken.Negation)
            {
                token = scanner.Scan();
                expression = new Negation(Fluent());
            }
            else
                expression = Fluent();

            return expression;
        }
        private IBooleanExpression And()
        {
            IBooleanExpression left = Other();
            if (token.kind == QueryToken.And)
            {
                token = scanner.Scan();
                return new And(left, And());
            }

            return left;
        }

        private IBooleanExpression Other()
        {
            IBooleanExpression left = Negation();
            if (token.kind == QueryToken.Iif)
            {
                token = scanner.Scan();
                return new Iif(left, Negation());
            }
            if (token.kind == QueryToken.Implication)
            {
                token = scanner.Scan();
                return new Implication(left, Negation());
            }
            return left;
        }

        private IBooleanExpression Fluent()
        {
            IBooleanExpression expression;
            if (token.kind == QueryToken.Open_par)
            {
                expression = Expression();
                if (token.kind != QueryToken.Close_par)
                {
                    throw new ParserException(string.Format("Closing parenthese expected at {0}", scanner.Pos));
                }
            }
            else
                if (token.kind == QueryToken.Ident)
                {
                    if(!graph.World.ContainsFluent(token.val))
                        throw new ParserException(string.Format("Not known fluent name at {0}", scanner.Pos));
                    expression = new Fluent(token.val);
                }
                else
                    throw new ParserException(string.Format("Fluent name or open parenthese expected at {0}", scanner.Pos));
                    
            token = scanner.Scan();
            return expression;
        }

         

    }

}
